#!/usr/bin/env python3
import json
import os
from pathlib import Path
import time
import paramiko
import logging
from stat import S_ISDIR

# 配置日志
logging.basicConfig(
    level=logging.INFO,  # 设置默认日志级别为 INFO
    format='%(asctime)s|%(levelname)-8s|%(lineno)-4u|%(message)s',  # 日志格式
    handlers=[
        logging.FileHandler("csms_download.log"),  # 将日志写入文件
        logging.StreamHandler()  # 同时输出到控制台（可选）
    ]
)

def load_config(config_file):
    """加载配置文件"""
    try:
        with open(config_file, 'r') as f:
            config = json.load(f)
        return config
    except Exception as e:
        logging.error(f"error loading configuration: {e}")
        exit(1)

def connect_sftp(config):
    while True:
        try:
            transport = paramiko.Transport((config["sftp_server"]["host"], config["sftp_server"]["port"]))
            transport.connect(username=config["sftp_server"]["username"], password=config["sftp_server"]["password"])
            sftp = paramiko.SFTPClient.from_transport(transport)
            logging.info("connected to SFTP server successfully.")
            return sftp, transport
        except Exception as e:
            logging.error(f"failed to connect to SFTP server: {e}")
            logging.info("retrying connection in 10 seconds...")
            time.sleep(10)  # 等待 5 秒后重试

def is_sftp_connected(sftp):
    """检查 SFTP 连接是否正常"""
    try:
        # 尝试执行一个简单的操作来验证连接是否正常
        sftp.listdir('.')
        return True
    except Exception:
        return False

def download_files(sftp, remote_path, local_dir):
    """递归下载远程目录中的文件，并删除已下载的文件"""
    try:
        # 确保本地目录存在
        Path(local_dir).mkdir(parents=True, exist_ok=True)
        #logging.info(f"Processing remote path: {remote_path}")

        # 检查路径是文件还是目录
        stat = sftp.stat(remote_path)
        if S_ISDIR(stat.st_mode):
            for entry in sftp.listdir_attr(remote_path):
                sub_remote_path = os.path.join(remote_path, entry.filename)
                sub_local_path = os.path.join(local_dir, entry.filename)
                download_files(sftp, sub_remote_path, local_dir)  # 递归处理
        else: 
            # 下载远程文件
            logging.info(f"find remote file: {remote_path}")
            local_file = os.path.join(local_dir, os.path.basename(remote_path))
            temp_file = local_file + ".tmp"
            try: 
                sftp.get(remote_path, temp_file)
                os.rename(temp_file, local_file)
            except Exception as e:
                logging.error(f"error downloading or renaming file '{remote_path}': {e}")
                if os.path.exists(temp_file):
                    os.remove(temp_file)  # 删除临时文件以防残留
                raise  # 重新抛出异常以便上层处理

            # 删除远程文件
            sftp.remove(remote_path)
            logging.info(f"down file: {remote_path} -> {local_dir}")
    except FileNotFoundError:
        pass
    except Exception as e:
        logging.error(f"error processing remote path '{remote_path}': {e}")

def scan_and_download(sftp, config):
    """每隔 10 秒扫描一次远程目录，并下载文件"""
    while True:
        logging.info("scanning......")
        for task in config['download_tasks']:
            remote_path = task['remote_dir']
            local_dir = task['local_dir']

            # 检查远程路径是否存在
            try:
                sftp.stat(remote_path)
            except FileNotFoundError:
                continue
            except Exception as e:
                logging.error(f"error accessing remote path '{remote_path}': {e}")
                continue

            # 扫描并下载文件
            download_files(sftp, remote_path, local_dir)

        #gging.info("Scan completed. Waiting for 10 seconds before next scan...")
        time.sleep(10)  # 每隔 10 秒扫描一次

def main():
    logging.info("mainapp start...")
    config = load_config("csms_download.json")
    # 初始化 SFTP 连接
    sftp, transport = connect_sftp(config)
    try:
        while True:
            # 检查连接状态，如果断开则重新连接
            if not is_sftp_connected(sftp):
                logging.warning("SFTP connection lost. Reconnecting...")
                try:
                    transport.close()
                except Exception:
                    pass  # 忽略关闭连接时的错误
                sftp, transport = connect_sftp(config)

            # 执行扫描和下载任务
            scan_and_download(sftp, config)
    except KeyboardInterrupt:
        logging.info("program terminated by user.")
    finally:
        # 关闭连接
        try:
            sftp.close()
            transport.close()
            logging.info("SFTP connection closed.")
        except Exception as e:
            logging.error(f"error closing SFTP connection: {e}")
        logging.info("mainapp end!!!")

if __name__ == "__main__":
    main()
